﻿// Decompiled with JetBrains decompiler
// Type: Netick.Entity
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;

#nullable disable
namespace Netick;

/// <summary>Represents an networked entity (object) in Netick.</summary>
[IgnoreCodeGen]
public sealed class Entity
{
  public readonly NetickEngine Engine;
  public INetickEntity UserEntity;
  public NetickUniqueObjectMeta ObjectMeta;
  public int BehsHash;
  public int UserId;
  public Vector3 Position;
  public int WorldIndex = -1;
  public int GroupTableId = -1;
  internal int SafeId = -1;
  public int RawId = -1;
  public bool IsMarkedForDestruction;
  internal bool IsReady;
  internal bool IsInSimulation;
  internal int AckTickFloor = -1;
  internal bool ChangeFlag;
  internal bool HasFullState = true;
  public long StateSizeWords;
  public long StateOffsetBytes;
  public int BlockIndex = -1;
  public unsafe int* StatePtr;
  internal unsafe int* StatePreviousPtr;
  internal unsafe void* MetaPtr;
  internal unsafe int** SH;
  internal unsafe Change* C;
  internal unsafe int* Pool;
  internal unsafe Handle* K;
  internal int _changesLen;
  internal int _changesWithClientsLen;
  internal int _poolSize;
  internal unsafe long* _changes;
  internal unsafe long* _changesWithClients;
  internal unsafe int* _dirties;
  internal unsafe int* _indexToChange;
  internal int _cH;
  internal int _cT;
  internal long _cI;
  internal AreaOfInterestLayer AoILayer;
  internal NarrowphaseClientData[] NarrowInterests;
  internal bool NarrowPhaseFiltered;
  internal bool _interestEnabled;
  internal InterestManagementGroup InternalInterestGroup;
  internal bool _isOnPredictedList;
  internal int _inputSourceIndexForEvent = -1;
  internal int _inputSourceIndex = -1;
  private NetworkPlayer _inputSourceCached;

  public AdaptiveRemoteInterpolation AdaptiveRemoteInterpolation
  {
    get => (AdaptiveRemoteInterpolation) null;
  }

  public int Id => this.SafeId;

  internal unsafe Entity(
    NetickEngine engine,
    INetickEntity userEntity,
    ISafeAllocator metaAllocator,
    IAllocator stateAllocator,
    long stateSizeWord,
    bool isWorldState,
    bool regularEntity,
    bool callInit,
    bool isAoiEntity = false)
  {
    this.Engine = engine;
    this.UserEntity = userEntity;
    this.StateSizeWords = stateSizeWord;
    ulong num1 = (ulong) (sizeof (Handle) * engine.MaxClients);
    ulong num2 = (ulong) (sizeof (int**) * engine._config.MaxSnapshots);
    long num3 = stateSizeWord + 2L;
    ulong num4 = (ulong) num3 * (ulong) sizeof (Change);
    ulong num5 = (ulong) num3 * 4UL;
    ulong num6 = (ulong) (num3 * 4L) * 2UL;
    ulong num7 = (ulong) (num3 * 4L) * 2UL;
    ulong num8 = (ulong) num3 * 4UL;
    ulong num9 = (ulong) num3 * 4UL;
    if (engine.IsClient)
    {
      num1 = 0UL;
      num2 = 0UL;
      num4 = 0UL;
      num5 = 0UL;
      num6 = 0UL;
      num7 = 0UL;
      num9 = 0UL;
    }
    ulong size = num4 + num5 + num6 + num7 + num8 + num9 + num1 + num2;
    this.MetaPtr = metaAllocator.SafeMalloc(size, out int _, out long _, !isAoiEntity);
    if (!isAoiEntity)
      this.StatePtr = (int*) stateAllocator.Allocate((ulong) stateSizeWord * 4UL, out this.BlockIndex, out this.StateOffsetBytes, false);
    byte* numPtr1 = (byte*) this.MetaPtr;
    if (engine.IsServer)
    {
      this.K = (Handle*) numPtr1;
      byte* numPtr2 = numPtr1 + num1;
      this.SH = (int**) numPtr2;
      byte* numPtr3 = numPtr2 + num2;
      this.C = (Change*) numPtr3;
      byte* numPtr4 = numPtr3 + num4;
      this.Pool = (int*) numPtr4;
      byte* numPtr5 = numPtr4 + num5;
      this._changes = (long*) numPtr5;
      byte* numPtr6 = numPtr5 + num6;
      this._changesWithClients = (long*) numPtr6;
      byte* numPtr7 = numPtr6 + num7;
      this._indexToChange = (int*) numPtr7;
      numPtr1 = numPtr7 + num9;
    }
    this._dirties = (int*) numPtr1;
    byte* numPtr8 = numPtr1 + num8;
    if (!isWorldState && this.Engine.IsServer)
    {
      for (int index = 0; index < this.Engine._config.MaxSnapshots; ++index)
        this.SH[index] = (int*) ((IntPtr) this.Engine._srvSim.Snapshots[index].Blocks[this.BlockIndex].Ptr + (IntPtr) this.StateOffsetBytes);
    }
    if (regularEntity)
      this.StatePreviousPtr = (int*) ((IntPtr) this.Engine.Simulation.PreviousPools[this.BlockIndex].Ptr + (IntPtr) this.StateOffsetBytes);
    if (!callInit)
      return;
    this.Init(-1, -1);
  }

  internal unsafe void Init(int worldIndex, int networkId)
  {
    this.RawId = -1;
    this.SafeId = -1;
    this.WorldIndex = -1;
    this.GroupTableId = -1;
    this.IsMarkedForDestruction = false;
    this.IsInSimulation = false;
    this.IsReady = false;
    this.HasFullState = true;
    this.ChangeFlag = false;
    this.Position = new Vector3();
    this.AckTickFloor = -1;
    this._changesWithClientsLen = 0;
    this._changesLen = 0;
    this._poolSize = 0;
    this._cH = -1;
    this._cT = -1;
    this._cI = 1L;
    this._interestEnabled = true;
    this.NarrowPhaseFiltered = false;
    this.AoILayer = (AreaOfInterestLayer) null;
    this._inputSourceCached = (NetworkPlayer) null;
    this._inputSourceIndexForEvent = -1;
    this._inputSourceIndex = -1;
    this._isOnPredictedList = false;
    this.AdaptiveRemoteInterpolation?.Reset();
    if (this.UserEntity != null && !(this.UserEntity is DummyEntity) && this.Engine._config.EnableInterestManagement)
    {
      if (this.UserEntity.BroadPhaseFilter == BroadPhaseFilter.AreaOfInterest)
      {
        if (this.UserEntity.AoILayer > this.Engine.Config.AoILayerCount - 1)
          NetickLogger.LogError((object) $"{this.UserEntity.ToString()}: AoI layer index [{this.UserEntity.AoILayer}] is out of bounds.");
        else
          this.AoILayer = this.Engine._IM.AoILayers[this.UserEntity.AoILayer];
      }
      if (this.UserEntity.NarrowPhaseFilter && this.Engine._config.EnableInterestManagement && this.Engine._config.EnableNarrowphaseFiltering)
      {
        for (int index = 0; index < this.NarrowInterests.Length; ++index)
          this.NarrowInterests[index] = NarrowphaseClientData.Default;
      }
    }
    if (this.UserEntity != null)
      MemoryAllocation.Clear((void*) this.StatePtr, this.StateSizeWords * 4L);
    int num = (int) (this.StateSizeWords + 2L);
    bool isClient = this.Engine.IsClient;
    for (int index = 0; index < num; ++index)
    {
      this._dirties[index] = 0;
      if (!isClient)
      {
        this._indexToChange[index] = -1;
        this.C[index].Clients = -1;
        this.Pool[this._poolSize++] = index;
      }
    }
    this.WorldIndex = worldIndex;
    this.RawId = networkId;
    this.SafeId = networkId;
    if (isClient)
      return;
    for (short index = 0; (int) index < this.Engine.MaxClients; ++index)
      *(this.K + index) = new Handle()
      {
        N = -1,
        P = -1,
        H = 0
      };
    this._cH = this.MC(-1, (short) 0);
    short index1 = -1;
    for (short index2 = 0; (int) index2 < this.Engine.MaxClients; ++index2)
    {
      Handle handle = new Handle();
      handle.SnapshotTick = -1;
      handle.H = this._cH;
      handle.N = -1;
      handle.P = (int) index1;
      if (index1 != (short) -1)
        this.K[index1].N = (int) index2;
      index1 = index2;
      this.K[index2] = handle;
    }
    this.C[this._cH].Clients = -1;
  }

  internal unsafe void ExpandMetaForAoiEntity(IAllocator metaAllocator)
  {
    void* metaPtr1 = this.MetaPtr;
    int stateSizeWords = (int) this.StateSizeWords;
    int num1 = (int) (this.StateSizeWords * 2L);
    ulong num2 = (ulong) (sizeof (Handle) * this.Engine.MaxClients);
    ulong num3 = (ulong) (sizeof (int**) * this.Engine._config.MaxSnapshots);
    int num4 = stateSizeWords + 2;
    int num5 = num1 + 2;
    ulong newByteSize1 = (ulong) (num5 * sizeof (Change));
    ulong newByteSize2 = (ulong) num5 * 4UL;
    ulong newByteSize3 = (ulong) num5 * 4UL * 2UL;
    ulong newByteSize4 = (ulong) num5 * 4UL * 2UL;
    ulong newByteSize5 = (ulong) num5 * 4UL;
    ulong newByteSize6 = (ulong) num5 * 4UL;
    ulong oldByteSize1 = (ulong) (num4 * sizeof (Change));
    ulong oldByteSize2 = (ulong) num4 * 4UL;
    ulong oldByteSize3 = (ulong) num4 * 4UL * 2UL;
    ulong oldByteSize4 = (ulong) num4 * 4UL * 2UL;
    ulong oldByteSize5 = (ulong) num4 * 4UL;
    ulong oldByteSize6 = (ulong) num4 * 4UL;
    ulong sizeBytes = newByteSize1 + newByteSize2 + newByteSize3 + newByteSize4 + newByteSize5 + newByteSize6 + num2 + num3;
    this.MetaPtr = metaAllocator.Allocate(sizeBytes, out int _, out long _, true);
    byte* metaPtr2 = (byte*) this.MetaPtr;
    this.K = (Handle*) this.ResizeMetaBuffer(ref metaPtr2, (void*) this.K, (int) num2, (int) num2);
    this.SH = (int**) this.ResizeMetaBuffer(ref metaPtr2, (void*) this.SH, (int) num3, (int) num3);
    this.C = (Change*) this.ResizeMetaBuffer(ref metaPtr2, (void*) this.C, (int) oldByteSize1, (int) newByteSize1);
    this.Pool = (int*) this.ResizeMetaBuffer(ref metaPtr2, (void*) this.Pool, (int) oldByteSize2, (int) newByteSize2);
    this._changes = (long*) this.ResizeMetaBuffer(ref metaPtr2, (void*) this._changes, (int) oldByteSize3, (int) newByteSize3);
    this._changesWithClients = (long*) this.ResizeMetaBuffer(ref metaPtr2, (void*) this._changesWithClients, (int) oldByteSize4, (int) newByteSize4);
    this._dirties = (int*) this.ResizeMetaBuffer(ref metaPtr2, (void*) this._dirties, (int) oldByteSize5, (int) newByteSize5);
    this._indexToChange = (int*) this.ResizeMetaBuffer(ref metaPtr2, (void*) this._indexToChange, (int) oldByteSize6, (int) newByteSize6);
    for (int index = num4; index < num5; ++index)
    {
      this._dirties[index] = 0;
      this._indexToChange[index] = -1;
      this.C[index].Clients = -1;
      this.Pool[this._poolSize++] = index;
    }
    metaAllocator.Deallocate(metaPtr1);
    this.StateSizeWords = (long) num1;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  private unsafe byte* ResizeMetaBuffer(
    ref byte* metaPtr,
    void* preBuffer,
    int oldByteSize,
    int newByteSize)
  {
    MemoryAllocation.Copy((void*) metaPtr, preBuffer, (long) oldByteSize);
    byte* numPtr = metaPtr;
    metaPtr += newByteSize;
    return numPtr;
  }

  internal void CreateUserEntityData(INetickEntity userEntity)
  {
    this.UserEntity = userEntity;
    if (this.UserEntity == null || this.UserEntity is DummyEntity || !this.Engine._config.EnableInterestManagement || !this.Engine._config.EnableNarrowphaseFiltering || !this.UserEntity.NarrowPhaseFilter || this.NarrowInterests != null)
      return;
    this.NarrowInterests = new NarrowphaseClientData[this.Engine.MaxClients];
  }

  internal void Reset()
  {
    INetickNetworkScript[] networkScripts = this.UserEntity.NetworkScripts;
    for (int index1 = 0; index1 < networkScripts.Length; ++index1)
    {
      for (int index2 = 0; index2 < networkScripts[index1].RelatedRpcs.Count; ++index2)
        networkScripts[index1].RelatedRpcs[index2].IsValid = false;
      networkScripts[index1].RelatedRpcs.Clear();
    }
  }

  public unsafe void SetStateFrom(
    byte* source,
    int offsetWords,
    int sizeWords,
    bool invokeOnChanged)
  {
    if (invokeOnChanged)
    {
      bool isServer = this.Engine.IsServer;
      Simulation sim = this.Engine._sim;
      ClientSimulation cliSim = this.Engine._cliSim;
      int isInputSourceOnly = 0;
      int* numPtr1 = this.StatePtr + offsetWords;
      int* numPtr2 = (int*) source;
      int* numPtr3 = this.StatePreviousPtr + offsetWords;
      bool flag = false;
      for (int index = 0; index < sizeWords; ++index)
      {
        int dataId = offsetWords + index;
        numPtr3[index] = numPtr1[index];
        if (numPtr1[index] != numPtr2[index])
        {
          numPtr1[index] = numPtr2[index];
          if (isServer)
            this.MarkAltered(numPtr1 + index, isInputSourceOnly);
          flag = true;
          sim.OnEntityStateChangedLocal(this, dataId, 0, this.StatePreviousPtr, true);
        }
      }
      if (!isServer & flag)
      {
        if (!this.ChangeFlag)
          cliSim.ChangedEntities.Add(this);
        this.ChangeFlag = true;
      }
      HashSet<OnChangedData> changesCache = this.Engine.Simulation._changesCache;
      foreach (OnChangedData changeInfo in changesCache)
      {
        try
        {
          changeInfo.Event((INetickScript) changeInfo.Behaviour, changeInfo);
        }
        catch (Exception ex)
        {
          INetickNetworkScript behaviour = changeInfo.Behaviour;
          NetickLogger.LogError((object) ex, (object) behaviour);
        }
      }
      changesCache.Clear();
    }
    else
      this.SetState((int*) source, this.StatePtr + offsetWords, sizeWords, 0, 0);
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  public unsafe void SetState(
    int* newValue,
    int* p,
    int size,
    int hasOnChanged,
    int isInputSourceOnly)
  {
    bool isServer = this.Engine.IsServer;
    int dataId = (int) (p - this.StatePtr);
    int* numPtr = this.StatePreviousPtr + dataId;
    bool flag = false;
    if (hasOnChanged == 1 && this.RawId != -1)
    {
      for (int index = 0; index < size; ++index)
      {
        numPtr[index] = p[index];
        if (p[index] != newValue[index])
        {
          p[index] = newValue[index];
          if (isServer)
            this.MarkAltered(p + index, isInputSourceOnly);
          flag = true;
        }
      }
      if (!flag)
        return;
      if (!isServer)
      {
        if (!this.ChangeFlag)
          this.Engine._cliSim.ChangedEntities.Add(this);
        this.ChangeFlag = true;
      }
      this.Engine._sim.OnEntityStateChangedLocal(this, dataId, 0, this.StatePreviousPtr, false);
    }
    else
    {
      for (int index = 0; index < size; ++index)
      {
        numPtr[index] = p[index];
        if (p[index] != newValue[index])
        {
          p[index] = newValue[index];
          if (isServer)
            this.MarkAltered(p + index, isInputSourceOnly);
          flag = true;
        }
      }
      if (!(!isServer & flag))
        return;
      if (!this.ChangeFlag)
        this.Engine._cliSim.ChangedEntities.Add(this);
      this.ChangeFlag = true;
    }
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  public unsafe void SetState_Collection(
    int* newValue,
    int* p,
    int size,
    int hasOnChanged,
    int isInputSourceOnly,
    int collectionHasOnChanged)
  {
    bool isServer = this.Engine.IsServer;
    int num = (int) (p - this.StatePtr);
    int* numPtr = this.StatePreviousPtr + num;
    bool flag = false;
    if (collectionHasOnChanged == 1)
    {
      for (int index = 0; index < size; ++index)
      {
        if (numPtr[index] != p[index])
        {
          numPtr[index] = p[index];
          NetworkCollectionChangedIds.Add(num);
        }
        if (p[index] != newValue[index])
        {
          p[index] = newValue[index];
          if (isServer)
            this.MarkAltered(p + index, isInputSourceOnly);
          flag = true;
        }
      }
    }
    else
    {
      for (int index = 0; index < size; ++index)
      {
        if (p[index] != newValue[index])
        {
          p[index] = newValue[index];
          if (isServer)
            this.MarkAltered(p + index, isInputSourceOnly);
          flag = true;
        }
      }
    }
    if (!flag)
      return;
    if (!isServer)
    {
      if (!this.ChangeFlag)
        this.Engine._cliSim.ChangedEntities.Add(this);
      this.ChangeFlag = true;
    }
    if (hasOnChanged != 1 || this.RawId == -1)
      return;
    this.Engine._sim.OnEntityStateChangedLocal(this, num, 0, this.StatePreviousPtr, false);
    for (int index = 0; index < NetworkCollectionChangedIds.Count; ++index)
    {
      int changedId = NetworkCollectionChangedIds.ChangedIds[index];
      if ((long) changedId < this.StateSizeWords)
        this.StatePreviousPtr[changedId] = this.StatePtr[changedId];
    }
    NetworkCollectionChangedIds.Clear();
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  public unsafe bool MarkAltered(int* p, int isInputSourceOnly = 0)
  {
    int index = (int) (p - this.StatePtr);
    if (this._dirties[index] == 1)
      return false;
    this._dirties[index] = 1;
    if (this._indexToChange[index] != -1 && this.C[this._indexToChange[index]].Clients != -1)
    {
      *(int*) (this._changesWithClients + this._changesWithClientsLen) = index;
      *(int*) ((byte*) (this._changesWithClients + this._changesWithClientsLen) + 4) = isInputSourceOnly;
      ++this._changesWithClientsLen;
    }
    else
    {
      *(int*) (this._changes + this._changesLen) = index;
      *(int*) ((byte*) (this._changes + this._changesLen) + 4) = isInputSourceOnly;
      ++this._changesLen;
    }
    if (this.GroupTableId != -1)
      this.InternalInterestGroup.Entity.InterestMarkAltered(this.GroupTableId);
    return true;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  public unsafe bool InterestMarkAltered(int index, int isInputSourceOnly = 0)
  {
    if (this._dirties[index] == 1)
      return false;
    this._dirties[index] = 1;
    if (this._indexToChange[index] != -1 && this.C[this._indexToChange[index]].Clients != -1)
    {
      *(int*) (this._changesWithClients + this._changesWithClientsLen) = index;
      *(int*) ((byte*) (this._changesWithClients + this._changesWithClientsLen) + 4) = isInputSourceOnly;
      ++this._changesWithClientsLen;
    }
    else
    {
      *(int*) (this._changes + this._changesLen) = index;
      *(int*) ((byte*) (this._changes + this._changesLen) + 4) = isInputSourceOnly;
      ++this._changesLen;
    }
    return true;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  internal unsafe void ProcessForGroup()
  {
    for (int index = 0; index < this._changesLen; ++index)
    {
      int* numPtr = (int*) (this._changes + index % this._changesLen);
      if (*numPtr != -1)
        this.CC(this.MC(*numPtr, (short) numPtr[1]));
    }
    for (int index = 0; index < this._changesWithClientsLen; ++index)
    {
      int* numPtr = (int*) (this._changesWithClients + index % this._changesWithClientsLen);
      if (*numPtr != -1)
        this.CC(this.MC(*numPtr, (short) numPtr[1]));
    }
    this._changesLen = 0;
    this._changesWithClientsLen = 0;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  internal unsafe void Process()
  {
    for (int index = 0; index < this._changesLen; ++index)
    {
      int* numPtr = (int*) (this._changes + index % this._changesLen);
      this.CC(this.MC(*numPtr, (short) numPtr[1]));
    }
    for (int index = 0; index < this._changesWithClientsLen; ++index)
    {
      int* numPtr = (int*) (this._changesWithClients + index % this._changesWithClientsLen);
      this.CC(this.MC(*numPtr, (short) numPtr[1]));
    }
    this._changesLen = 0;
    this._changesWithClientsLen = 0;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  private unsafe int MC(int index, short isInputSourceOnly)
  {
    int index1 = this.Pool[--this._poolSize];
    this.C[index1].Index = index;
    this.C[index1].I = this._cI;
    this.C[index1].P = -1;
    this.C[index1].N = -1;
    this.C[index1].IsInputSourceOnly = (int) isInputSourceOnly;
    ++this._cI;
    return index1;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  internal unsafe void TRC(int index)
  {
    int index1 = this._indexToChange[index];
    if (index1 == -1)
      return;
    if (this.C[index1].Clients != -1)
    {
      int p = this.C[index1].P;
      int index2 = this.C[index1].Clients;
      int index3 = index2;
      for (; index2 != -1; index2 = this.K[index2].N)
      {
        this.K[index2].H = p;
        index3 = index2;
      }
      if (this.C[p].Clients != -1)
      {
        this.K[this.C[p].Clients].P = index3;
        this.K[index3].N = this.C[p].Clients;
      }
      this.C[p].Clients = this.C[index1].Clients;
    }
    if (this._cT == index1)
      this._cT = this.C[index1].P;
    this.C[this.C[index1].P].N = this.C[index1].N;
    if (this.C[index1].N != -1)
      this.C[this.C[index1].N].P = this.C[index1].P;
    this.C[index1].Clients = -1;
    this.Pool[this._poolSize++] = index1;
    this._indexToChange[index] = -1;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  private unsafe void CC(int newChange)
  {
    int index = this.C[newChange].Index;
    this.TRC(index);
    this._indexToChange[index] = newChange;
    if (this._cT == -1)
    {
      this._cT = newChange;
      this.C[this._cH].N = newChange;
      this.C[newChange].P = this._cH;
    }
    else
    {
      this.C[this._cT].N = newChange;
      this.C[newChange].P = this._cT;
      this._cT = newChange;
    }
    this._dirties[index] = 0;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  internal unsafe void AP(int client, long rangeEnd, int tick)
  {
    if (rangeEnd == -1L)
      this.NarrowInterests[client].AckTick = tick;
    Handle handle = this.K[client];
    int h = handle.H;
    if (this.C[h].I >= rangeEnd)
      return;
    int newNode;
    int n;
    for (newNode = handle.H; newNode != -1; newNode = n)
    {
      n = this.C[newNode].N;
      if (n == -1 || this.C[n].I >= rangeEnd)
        break;
    }
    this.MV(client, newNode, h, tick);
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  internal unsafe void MV(int c, int newNode, int olderNode, int tick)
  {
    if (tick > this.AckTickFloor)
      this.K[c].SnapshotTick = tick;
    if (newNode == -1 || newNode == olderNode)
      return;
    if (this.C[olderNode].Clients == c)
    {
      this.C[olderNode].Clients = this.K[c].N;
      if (this.K[c].N != -1)
        this.K[this.K[c].N].P = -1;
      this.K[c].P = -1;
      this.K[c].N = -1;
    }
    else
    {
      if (this.K[c].P != -1)
        this.K[this.K[c].P].N = this.K[c].N;
      if (this.K[c].N != -1)
        this.K[this.K[c].N].P = this.K[c].P;
    }
    if (this.C[newNode].Clients != -1)
    {
      this.K[this.C[newNode].Clients].P = c;
      this.K[c].N = this.C[newNode].Clients;
      this.K[c].P = -1;
      this.C[newNode].Clients = c;
    }
    else
    {
      this.C[newNode].Clients = c;
      this.K[c].N = -1;
      this.K[c].P = -1;
    }
    this.K[c].H = newNode;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  internal unsafe void ResetForClient(int client)
  {
    this.MV(client, this._cH, this.K[client].H, -1);
    if (this.NarrowInterests == null)
      return;
    this.NarrowInterests[client] = NarrowphaseClientData.Default;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  internal unsafe void ResetForClient_InputSource(int client)
  {
    this.MV(client, this._cH, this.K[client].H, -1);
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  internal unsafe void ResetSnapshotTicks()
  {
    this.AckTickFloor = (int) this.Engine._simulationTick;
    for (int index = 0; index < this.Engine.MaxClients; ++index)
      this.K[index].SnapshotTick = -1;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  internal unsafe int GetClientNodeToWrite(int client) => this.C[this.K[client].H].N;

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  internal unsafe bool WP(ref WriteData w, bool writeDelta = false)
  {
    int index1 = w.LastNode;
    int num1 = 0;
    uint num2 = 1;
    int num3 = 0;
    w.Buffer.Add(1, writeDelta ? 1U : 0U);
    if (writeDelta)
    {
      int* numPtr = this.SH[w.SnapshotIndex];
      for (; index1 != -1; index1 = this.C[index1].N)
      {
        if (w.MTUBits < w.Buffer.WritePos)
        {
          num2 = 0U;
          break;
        }
        if (this.C[index1].IsInputSourceOnly != 1 || w.IsInputSource)
        {
          int index2 = this.C[index1].Index;
          w.Buffer.AddInt4(index2 - num3);
          w.Buffer.AddInt4(this.StatePtr[index2] - numPtr[index2]);
          num3 = index2;
          ++num1;
        }
        w.LastNode = index1;
      }
    }
    else
    {
      for (; index1 != -1; index1 = this.C[index1].N)
      {
        if (w.MTUBits < w.Buffer.WritePos)
        {
          num2 = 0U;
          break;
        }
        if (this.C[index1].IsInputSourceOnly != 1 || w.IsInputSource)
        {
          int index3 = this.C[index1].Index;
          w.Buffer.AddInt4(index3 - num3);
          w.Buffer.AddInt(this.StatePtr[index3]);
          num3 = index3;
          ++num1;
        }
        w.LastNode = index1;
      }
    }
    w.Buffer.AddInt4(num1 == 0 ? -1 : 0);
    w.WrittenRangeEnd = this.C[w.LastNode].I;
    return num2 == 1U;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  internal unsafe bool WPInputOnly(ref WriteData w, bool writeDelta = false)
  {
    int index1 = w.LastNode;
    int num1 = 0;
    uint num2 = 1;
    int num3 = 0;
    w.Buffer.Add(1, writeDelta ? 1U : 0U);
    if (writeDelta)
    {
      int* numPtr = this.SH[w.SnapshotIndex];
      for (; index1 != -1; index1 = this.C[index1].N)
      {
        if (w.MTUBits < w.Buffer.WritePos)
        {
          num2 = 0U;
          break;
        }
        if (this.C[index1].IsInputSourceOnly == 1)
        {
          int index2 = this.C[index1].Index;
          w.Buffer.AddInt4(index2 - num3);
          w.Buffer.AddInt4(this.StatePtr[index2] - numPtr[index2]);
          num3 = index2;
          ++num1;
        }
        w.LastNode = index1;
      }
    }
    else
    {
      for (; index1 != -1; index1 = this.C[index1].N)
      {
        if (w.MTUBits < w.Buffer.WritePos)
        {
          num2 = 0U;
          break;
        }
        if (this.C[index1].IsInputSourceOnly == 1)
        {
          int index3 = this.C[index1].Index;
          w.Buffer.AddInt4(index3 - num3);
          w.Buffer.AddInt(this.StatePtr[index3]);
          num3 = index3;
          ++num1;
        }
        w.LastNode = index1;
      }
    }
    w.Buffer.AddInt4(num1 == 0 ? -1 : 0);
    w.WrittenRangeEnd = this.C[w.LastNode].I;
    return num2 == 1U;
  }

  public bool IsInterested => this._interestEnabled;

  public InterestManagementGroup InterestGroup
  {
    get => this.InternalInterestGroup;
    set
    {
      if (this.Engine.IsClient)
        throw new NotServerException();
      if (this.UserEntity.BroadPhaseFilter != BroadPhaseFilter.Custom || !this.Engine._config.EnableInterestManagement)
        return;
      this.Engine._IM.ServerSetEntityInterestGroup(this, value);
    }
  }

  public void Move(Vector3 newPos) => this.AoILayer?.Move(this, newPos);

  public void SetNarrowphaseInterest(NetworkPlayer player, bool isInterested)
  {
    ServerConnection serverConnection = player as ServerConnection;
    if (this.InternalInterestGroup == null)
    {
      NetickLogger.LogError(this.Engine, (object) "An object must have an interest group before you can call SetNarrowphaseInterest on it.");
    }
    else
    {
      if (serverConnection == null || this.NarrowInterests == null)
        return;
      int changeTick = this.NarrowInterests[serverConnection.Index].ChangeTick;
      if (!isInterested && changeTick != int.MaxValue || isInterested && changeTick == int.MaxValue)
        return;
      this.NarrowInterests[serverConnection.Index].ChangeTick = isInterested ? int.MaxValue : (int) (this.Engine.Tick + 1);
      this.InternalInterestGroup.Entity.InterestMarkAltered(this.GroupTableId);
    }
  }

  public bool IsPredicted
  {
    [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this._isOnPredictedList;
  }

  internal int InternalInputSourceIndex
  {
    get => this._inputSourceIndex;
    set
    {
      this._inputSourceIndex = value;
      this._inputSourceCached = this.InternalInputSource;
    }
  }

  public NetworkPlayerId InputSourcePlayerId
  {
    get
    {
      if (this._inputSourceIndex == -1)
        return NetworkPlayerId.Create(-1);
      return this._inputSourceIndex == -2 ? NetworkPlayerId.Create(0) : NetworkPlayerId.Create(this._inputSourceIndex + 1);
    }
  }

  public NetworkPlayer InputSource
  {
    [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this._inputSourceCached;
    set
    {
      this.InternalInputSource = value;
      this._inputSourceCached = this.InternalInputSource;
    }
  }

  internal NetworkPlayer InternalInputSource
  {
    [MethodImpl(MethodImplOptions.AggressiveInlining)] get
    {
      if (this.InternalInputSourceIndex == -1)
        return (NetworkPlayer) null;
      return this.Engine.IsServer ? (this.InternalInputSourceIndex == -2 ? (NetworkPlayer) this.Engine._peer : (NetworkPlayer) this.Engine._server.Clients[this.InternalInputSourceIndex]) : (this.InternalInputSourceIndex == this.Engine._cliSim.LocalInputSourceIndex ? (NetworkPlayer) this.Engine._peer : (NetworkPlayer) null);
    }
    set => this.SetInputSource(value, true);
  }

  internal void SetInputSource(NetworkPlayer player, bool invokeOnInputSourceChanged)
  {
    if (this.Engine.IsClient)
      throw new NotServerException();
    NetworkPlayer inputSource1 = this.InputSource;
    if (inputSource1 == player)
      return;
    if (player == null)
    {
      if (inputSource1 != null)
        inputSource1.ControlledEntities.RemoveUnordered(this);
      NetworkPlayer inputSource2 = this.InputSource;
      this.InternalInputSourceIndex = -1;
      this.Engine._entityMetaReplicator.SetInputSource(this.WorldIndex, -1);
      if (!invokeOnInputSourceChanged || !this.IsReady)
        return;
      this.InvokeOnInputSourceChanged(inputSource2);
    }
    else
    {
      if (inputSource1 != null)
        inputSource1.ControlledEntities.RemoveUnordered(this);
      player?.ControlledEntities.Add(this);
      NetworkPlayer inputSource3 = this.InputSource;
      ServerConnection serverConnection = player as ServerConnection;
      int inputSourceIndex = -2;
      if (serverConnection != null)
        inputSourceIndex = serverConnection.Index;
      this.InternalInputSourceIndex = inputSourceIndex;
      this.Engine._entityMetaReplicator.SetInputSource(this.WorldIndex, inputSourceIndex);
      if (invokeOnInputSourceChanged && this.IsReady)
        this.InvokeOnInputSourceChanged(inputSource3);
      if (serverConnection == null)
        return;
      this.ResetForClient_InputSource(serverConnection.Index);
    }
  }

  internal void SetInputSourceAsNull()
  {
    this.InternalInputSourceIndex = -1;
    this.Engine._entityMetaReplicator.SetInputSource(this.WorldIndex, -1);
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  public static unsafe void InternalDirtify(
    INetickNetworkScript script,
    int* data,
    int* p,
    int sizeInWords,
    int hasOnChanged,
    int isInputSourceOnly = 0)
  {
    script.Entity.SetState(data, p, sizeInWords, hasOnChanged, isInputSourceOnly);
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  public static unsafe void InternalRegOnChanged(
    INetickNetworkScript script,
    int isArray,
    int invokeDuringResims,
    int inversePrecision,
    int* propertyStart,
    int* propertyPartIndex,
    int propertySizeWords,
    OnChangedEvent onChanged)
  {
    Entity entity = script.Entity;
    int propertyIndex = (int) (propertyStart - entity.StatePtr);
    int index = (int) (propertyPartIndex - entity.StatePtr);
    entity.ObjectMeta.PropertyElementsToPropertyStartIndex[index] = new OnChangedMethod(isArray == 1, inversePrecision, invokeDuringResims == 1, propertyIndex, propertySizeWords, script.Index, onChanged);
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  public static void InternalRegSmooth(
    INetickNetworkScript script,
    string propertyName,
    int offsetInWords,
    float precision,
    int vectorFloatFieldsCount)
  {
    Entity entity = script.Entity;
    string key = $"{script.GetType().FullName}_{propertyName}";
    if (entity.ObjectMeta.PropertyNameToSmoothMetaData.ContainsKey(key))
      return;
    entity.ObjectMeta.PropertyNameToSmoothMetaData.Add(key, new SmoothMetaData()
    {
      DataOffsetWords = offsetInWords,
      Precision = precision,
      VectorFloatFieldsCount = vectorFloatFieldsCount
    });
  }

  public static int GetStateSizeWords(INetickNetworkScript[] scripts)
  {
    int val2 = 0;
    for (int index = 0; index < scripts.Length; ++index)
      val2 += scripts[index].InternalGetStateSizeWords();
    return Math.Max(1, val2);
  }

  internal void CalculateBehsHash()
  {
    INetickNetworkScript[] networkScripts = this.UserEntity.NetworkScripts;
    string str = "";
    for (int index = 0; index < networkScripts.Length; ++index)
      str += networkScripts[index].GetType().FullName;
    this.BehsHash = str.GetStableHashCode();
    NetickUniqueObjectMeta uniqueObjectMeta;
    if (this.Engine._reflectionData.HashToObjectMetaData.TryGetValue(this.BehsHash, out uniqueObjectMeta))
    {
      this.ObjectMeta = uniqueObjectMeta;
    }
    else
    {
      this.ObjectMeta = new NetickUniqueObjectMeta();
      this.ObjectMeta.PropertyElementsToPropertyStartIndex = new OnChangedMethod[this.StateSizeWords];
      for (int index = 0; index < networkScripts.Length; ++index)
        networkScripts[index].InternalInit();
      this.Engine._reflectionData.HashToObjectMetaData.Add(this.BehsHash, this.ObjectMeta);
    }
  }

  public EntityStateRef GetStateRef()
  {
    return new EntityStateRef((int) this.StateSizeWords * 4, this.StateOffsetBytes, this.BlockIndex);
  }

  public bool IsInputSource => this.InputSource == this.Engine._peer;

  public bool IsProxy => this.Engine.IsClient && !this.IsInputSource;

  public bool IsOwner => this.Engine.IsServer;

  public bool IsResimulating => this.Engine.IsResimulating;

  internal bool UseAoI => this.UserEntity.BroadPhaseFilter == BroadPhaseFilter.AreaOfInterest;

  internal void ApplyDefaultState(int networkId)
  {
    INetickNetworkScript[] networkScripts = this.UserEntity.NetworkScripts;
    this.RawId = -1;
    for (int index = 0; index < networkScripts.Length; ++index)
      networkScripts[index].InternalReset();
    this.RawId = networkId;
  }

  internal void InvokeNetworkAwake()
  {
    foreach (INetickNetworkScript networkScript in this.UserEntity.NetworkScripts)
    {
      try
      {
        networkScript.NetworkAwake();
      }
      catch (Exception ex)
      {
        NetickLogger.LogError(this, (object) ex);
      }
    }
  }

  internal void InvokeOnInputSourceChanged(NetworkPlayer previous)
  {
    foreach (INetickNetworkScript networkScript in this.UserEntity.NetworkScripts)
    {
      try
      {
        networkScript.OnInputSourceChanged(previous);
      }
      catch (Exception ex)
      {
        NetickLogger.LogError(this, (object) ex);
      }
    }
  }

  internal void InvokeOnInputSourceLeft()
  {
    foreach (INetickNetworkScript networkScript in this.UserEntity.NetworkScripts)
    {
      try
      {
        networkScript.OnInputSourceLeft();
      }
      catch (Exception ex)
      {
        NetickLogger.LogError(this, (object) ex);
      }
    }
  }

  internal void InvokeOnBecameInterested()
  {
    foreach (INetickNetworkScript networkScript in this.UserEntity.NetworkScripts)
    {
      try
      {
        networkScript.OnBecameInterested();
      }
      catch (Exception ex)
      {
        NetickLogger.LogError(this, (object) ex);
      }
    }
  }

  internal void InvokeOnBecameUninterested()
  {
    foreach (INetickNetworkScript networkScript in this.UserEntity.NetworkScripts)
    {
      try
      {
        networkScript.OnBecameUninterested();
      }
      catch (Exception ex)
      {
        NetickLogger.LogError(this, (object) ex);
      }
    }
  }

  public void InvokeGameEngineIntoNetcode()
  {
    foreach (INetickScript networkScript in this.UserEntity.NetworkScripts)
      networkScript.GameEngineIntoNetcode();
  }

  public void InvokeNetcodeIntoGameEngine()
  {
    foreach (INetickScript networkScript in this.UserEntity.NetworkScripts)
      networkScript.NetcodeIntoGameEngine();
  }
}
